﻿using System;
using System.Json;
using System.Linq;
using ToodledoAPI.DataTypes;
using ToodledoAPI.Exceptions;

namespace ToodledoAPI
{
    public static class tasks
    {
        public static void get(Key key, DateTime ModAfter, Action<Task[]> returnAction)
        {
            string Method = String.Format("tasks/get.php?key={0};modafter={1}; fields={2}", key, (UnixTime)ModAfter, ApplicationConfiguration.taskDetailsConfiguration.fields());
            ApplicationConfiguration.CallWebService(Method, (s) =>
            {
                if (s.IsToodledoException())
                {
                    throw new taskException(s);
                }
                JsonArray Array = (JsonArray)JsonObject.Parse(s);
                returnAction(Array.Select((F) => new Task((JsonObject)F)).ToArray());
            });
        }
    }

    namespace DataTypes
    {
        public class Task
        {
            public Task(string s)
            {
                if (s.IsToodledoException())
                    throw new taskException(s);
                Load(JsonTaskObject.Parse(s));
            }
            public Task(JsonObject G)
            {
                Load(new JsonTaskObject(G));
            }
            void Load(JsonTaskObject T)
            {
                id = T.LoadInt("id");
                title = T.LoadString("title");
                tag = T.LoadString("tag");
                folder = T.LoadInt("folder");
                context = T.LoadInt("context");
                goal = T.LoadInt("goal");
                location = T.LoadInt("location");
                parent = T.LoadInt("parent");
                children = T.LoadInt("children");
                order = T.LoadInt("order");
                duedate = T.LoadDateTime("duedate");
                duedatemod = T.LoadInt("duedatemod");
                startdate = T.LoadDateTime("startdate");
                duetime = T.LoadDateTime("duetime");
                starttime = T.LoadDateTime("starttime");
                remind = T.LoadInt("remind");
                repeat = T.LoadString("repeat");
                isRepeatFromCompletionDate = T.LoadBool("repeatfrom");
                status = T.LoadInt("status");
                length = T.LoadInt("length");
                isStared = T.LoadBool("star");
                modified = T.LoadDateTime("modified");
                completed = T.LoadDateTime("completed");
                added = T.LoadDateTime("added");
                timer = T.LoadInt("timer");
                timeron = T.LoadDateTime("timeron");
                note = T.LoadString("note");
                meta = T.LoadString("meta");
            }
            #region Fields
            /// <summary>
            /// The server id number for this task. 
            /// It is guaranteed to be unique per account, but two different accounts may have two different tasks with the same id number.
            /// </summary>
            public int id { get; set; }
            /// <summary>
            /// A string for the name of the task. Up to 255 characters. 
            /// When setting, please encode the & character as %26 and the ; character as %3B.
            /// </summary>
            public string title { get; set; }
            /// <summary>
            /// A comma separated string listing the tags assigned to this task. Up to 64 characters. 
            /// When setting, please encode the & character as %26 and the ; character as %3B.
            /// </summary>
            public string tag { get; set; }
            /// <summary>
            /// The id number of the folder. Omit this field or set it to 0 to leave the task unassigned to a folder.
            /// </summary>
            public int folder { get; set; }
            /// <summary>
            /// The id number of the context. Omit this field or set it to 0 to leave the task unassigned to a context.
            /// </summary>
            public int context { get; set; }
            /// <summary>
            /// The id number of the goal. Omit this field or set it to 0 to leave the task unassigned to a goal.
            /// </summary>
            public int goal { get; set; }
            /// <summary>
            /// The id number of the location. Omit this field or set it to 0 to leave the task unassigned to a location.
            /// </summary>
            public int location { get; set; }
            /// <summary>
            /// This is used on Pro accounts that have access to subtasks. To create a subtask, set this to the id number of the parent task. The default is 0, which creates a normal task.
            /// </summary>
            public int parent { get; set; }
            /// <summary>
            /// This is used on Pro accounts that have access to subtasks. This will indicate the number of child tasks that this task has. This will be 0 for subtasks or for regular tasks without subtasks.
            /// </summary>
            public int children { get; set; }
            /// <summary>
            /// This is used on Pro accounts that have access to subtasks. 
            /// This is an integer that indicates the manual order of subtasks within the parent task. Currently this is read-only.
            /// </summary>
            public int order { get; set; }
            /// <summary>
            /// A GMT unix timestamp for when the task is due. The time component of this timestamp will always be noon.
            /// </summary>
            public DateTime duedate { get; set; }
            /// <summary>
            /// An integer representing the due date modifier.
            /// 0 = Due By
            /// 1 = Due On (=)
            /// 2 = Due After (>)
            /// 3 = Optionally (?)
            /// </summary>
            public int duedatemod { get; set; }
            /// <summary>
            /// A GMT unix timestamp for when the task starts. The time component of this timestamp will always be noon.
            /// </summary>
            public DateTime startdate { get; set; }
            /// <summary>
            /// A GMT unix timestamp for when the task is due. If the task does not have a time set, then this will be 0. 
            /// If the task has a duetime without a duedate set, then the date component of this timestamp will be Jan 1, 1970.
            /// </summary>
            public DateTime duetime { get; set; }
            /// <summary>
            /// A GMT unix timestamp for when the task starts. If the task does not have a time set, then this will be 0. 
            /// If the task has a starttime without a startdate set, then the date component of this timestamp will be Jan 1, 1970.
            /// </summary>
            public DateTime starttime { get; set; }
            /// <summary>
            /// An integer that represents the number of minutes prior to the duedate/time that a reminder will be sent. Set it to 0 for no reminder. 
            /// Values will be constrained to this list of valid numbers: 
            /// (0, 1, 15, 30, 45, 60, 90, 120, 180, 240, 1440, 2880, 4320, 5760, 7200, 8640, 10080, 20160, 43200). 
            /// Additionally, if the user does not have a Pro account, the only valid numbers are 0,60. 
            /// If you submit an invalid number, it will be rounded up or down to a valid non zero value.
            /// </summary>
            public int remind { get; set; }
            /// <summary>
            /// A string indicating how the task repeats. 
            /// For example: "Every 1 Week" or "On the 2nd Friday". Please read our repeat format faq for details about valid values. 
            /// When a task is rescheduled, it is moved forward to the new date. 
            /// For record keeping purposes, a completed copy of this task will be added to the user's list. 
            /// It will have a new ID number and will be already completed. To unset this value, set it to an empty string.
            /// http://www.toodledo.com/info/help.php?sel=2
            /// </summary>
            public string repeat { get; set; }
            /// <summary>
            /// Indicates how tasks repeat. It will be false to repeat from the due-date or true to repeat from the completion date
            /// </summary>
            public bool isRepeatFromCompletionDate { get; set; }
            /// <summary>
            /// An integer that represents the status of the task.
            /// 0 = None
            /// 1 = Next Action
            /// 2 = Active
            /// 3 = Planning
            /// 4 = Delegated
            /// 5 = Waiting
            /// 6 = Hold
            /// 7 = Postponed
            /// 8 = Someday
            /// 9 = Canceled
            /// 10 = Reference
            /// </summary>
            public int status { get; set; }
            /// <summary>
            /// An integer representing the number of minutes that the task will take to complete.
            /// </summary>
            public int length { get; set; }
            /// <summary>
            /// An integer that represents the priority.
            /// -1 = Negative
            /// 0 = Low
            /// 1 = Medium
            /// 2 = High
            /// 3 = Top
            /// </summary>
            public int priority { get; set; }
            /// <summary>
            /// True if the task has a star
            /// </summary>
            public bool isStared { get; set; }
            /// <summary>
            /// A GMT unix timestamp for when the task was last modified.
            /// </summary>
            public DateTime modified { get; set; }
            /// <summary>
            /// A GMT unix timestamp for when the task was completed. 
            /// If the task is not completed, the value will be 0. 
            /// Toodledo does not track the time that a task was completed, so tasks will always appear to be completed at noon.
            /// </summary>
            public DateTime completed { get; set; }
            /// <summary>
            /// A GMT unix timestamp for when the task was added. 
            /// Toodledo does not track the time that a task was added, so tasks will always appear to be added at noon.
            /// </summary>
            public DateTime added { get; set; }
            /// <summary>
            /// The value in the timer field indicates the number of seconds that have elapsed for the timer not including the current session.
            /// </summary>
            public int timer { get; set; }
            /// <summary>
            /// If the timer is currently on, this will contain a unix timestamp indicating the last time that the timer was started. 
            /// Therefore, if the timer is currently on, you will need to calculate the elapsed time when you present it to the user. 
            /// This calculation is: Total Time=timer+(now-timeron). Where "now" is a unix timestamp for the current time.
            /// </summary>
            public DateTime timeron { get; set; }
            /// <summary>
            /// A text string up to 32,000 bytes long. When setting, please encode the & character as %26 and the ; character as %3B.
            /// </summary>
            public string note { get; set; }
            /// <summary>
            /// A text string up to 255 bytes long for storing metadata about the task. 
            /// This is useful for syncing data that cannot otherwise be synced to Toodledo. 
            /// This data is unique per task ID. When a task is rescheduled, a new This data is private to your App ID. 
            /// Neither the user, nor other App IDs can see the data that you put in here. 
            /// Because of an implementation detail, using the meta field introduces extra latency to each API call, so you should only use this field when necessary.
            /// </summary>
            public string meta { get; set; }
            #endregion
        }

        internal class JsonTaskObject
        {
            JsonObject InnerObject;
            public JsonTaskObject(JsonObject O)
            { InnerObject = O; }
            public static JsonTaskObject Parse(string s)
            {
                return new JsonTaskObject((JsonObject)JsonObject.Parse(s));
            }

            public string LoadString(string PropertyName)
            {
                return LoadField(PropertyName, (s) => s);
            }
            public decimal LoadDecimal(string PropertyName)
            {
                return LoadField(PropertyName, decimal.Parse);
            }
            public int LoadInt(string PropertyName)
            {
                return LoadField(PropertyName, int.Parse);
            }
            public UnixTime LoadDateTime(string PropertyName)
            {
                return LoadField(PropertyName, long.Parse);
            }
            public bool LoadBool(string PropertyName)
            {
                return LoadField(PropertyName, (s) => s == "1");
            }


            public T LoadField<T>(string PropertyName, Func<string, T> Parse)
            {
                return ApplicationConfiguration.taskDetailsConfiguration.LoadField(InnerObject, PropertyName, Parse);
            }
        }
    }
}
